package vis.core.common.security;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson2.JSONArray;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import vis.core.common.properties.VisProSecurity;
import vis.core.common.util.StringKit;
import vis.core.domain.sys.config.SysConfig;

import java.util.List;

/**
 * [Sa-Token 权限认证] 配置类
 *
 * @author click33
 */
@Slf4j
@Configuration
public class AuthConfigure implements WebMvcConfigurer {

    @Autowired
    private VisProSecurity visProSecurity;

    @Autowired
    private AuthVisInterceptor authVisInterceptor;

    @Autowired
    private SysConfig sysConfig;

    /**
     * 1.拦截器配置
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册 Sa-Token 拦截器，打开注解式鉴权功能
        registry.addInterceptor(new SaInterceptor(handle -> {
            SaRouter.match("/**")
                    //放行路径，在配置文件中
                    .notMatch(excludePaths())
                    .check(r -> {
                        //先检查自定义权限
                        AuthPublic classAuthPublic = ((HandlerMethod) handle).getBeanType().getAnnotation(AuthPublic.class);
                        AuthPublic methodAuthPublic = ((HandlerMethod) handle).getMethod().getAnnotation(AuthPublic.class);
                        if (classAuthPublic != null || methodAuthPublic != null) return;
                        //非开放接口，继续检查
                        StpUtil.checkLogin();
                    });
        })).addPathPatterns("/**");

        //自定义实现，非注解开发时的校验规则
        registry.addInterceptor(authVisInterceptor).addPathPatterns("/**");

        log.info("========================= 拦截器注册完成 =========================");
    }

    // 动态获取哪些 path 可以忽略鉴权
    public List<String> excludePaths() {
        //先走数据库
        String s = sysConfig.resIgnore();
        if (StringKit.isNotBlank(s)) return JSONArray.parseArray(s, String.class);
        //数据库没有的话，走本地配置文件
        return visProSecurity.getResIgnore();
    }

    // Sa-Token 整合 jwt (Simple 简单模式)
    @Bean
    public StpLogic getStpLogicJwt() {
        return new StpLogicJwtForSimple();
    }

    /**
     * 注册 [Sa-Token 全局过滤器]
     */
    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()
                // 指定 [拦截路由] 与 [放行路由]
                .addInclude("/**").addExclude("/favicon.ico")
                // 前置函数：在每次认证函数之前执行
                .setBeforeAuth(obj -> {
                    SaHolder.getResponse()
                            // ---------- 设置跨域响应头 ----------
                            // 允许指定域访问跨域资源
                            .setHeader("Access-Control-Allow-Origin", "*")
                            // 允许所有请求方式
                            .setHeader("Access-Control-Allow-Methods", "*")
                            // 允许的header参数
                            .setHeader("Access-Control-Allow-Headers", "*")
                            // 有效时间
                            .setHeader("Access-Control-Max-Age", "3600");
                    // 如果是预检请求，则立即返回到前端
                    SaRouter.match(SaHttpMethod.OPTIONS).free(r -> {
                        //                        System.out.println("--------OPTIONS预检请求，不做处理");
                    }).back();
                });
    }

}
